---
id: dynamic
sidebar_position: 4
hide_table_of_contents: true
title: 动态 SQL
description: dbVisitor 工具提供提供基于 XML 动态 SQL 配置，并且采用了大家熟悉的 MyBatis 风格。
---
import TagRed from '@site/src/components/tags/TagRed';
import TagGray from '@site/src/components/tags/TagGray';

# 动态 SQL

当通过 Mapper XML 文件配置 SQL 时，允许通过 if、choose、foreach 等标签对 SQL进行动态配置。dbVisitor 采用了和 MyBatis 相同的风格。
- [&lt;if&gt; 标签](./dynamic#if)，用于判断某个条件满足。
- [&lt;choose&gt;、&lt;when&gt;、&lt;otherwise&gt; 标签](./dynamic#choose)，多分支条件选择。
- [&lt;trim&gt;、&lt;where&gt;、&lt;set&gt; 标签](./dynamic#trim)，三个标签可以帮助我们在生成特定 SQL 语句时不会造成纰漏。
- [&lt;foreach&gt; 标签](./dynamic#foreach)，foreach。
- [&lt;bind&gt; 标签](./dynamic#bind)，使用分页查询机制进行分页查询。

## if 标签 {#if}

&lt;if&gt; 用于判断某个条件满足之后拼接对应的 SQL。

```xml
<select id="queryUser">
    select * from users
    where state = 'ACTIVE'
    <if test="age != null">
        and age = #{age}
    </if>
</select>
```

:::info[单纯的 if 判断可以使用规则来替代，例如：]
```xml
<select id="queryUser">
    select * from users
    where state = 'ACTIVE' 
</select>
```
- 了解 [AND 规则](../../rules/dynamic_rule#and)
:::

### 标签属性

| 属性名  | 描述                                                     |
|------|--------------------------------------------------------|
| test | 必选，一个 ognl 表达式，表达式值是 boolean 表示判断是否成立，若判断成立则执行 SQL 生成。 |

## choose (when, otherwise) 标签 {#choose}

当有条件被满足之后会生成对应 &lt;when&gt; 中的 SQL，如果有多个条件同时匹配，只有第一个会生效。若没有匹配任何 &lt;when&gt; 那么 &lt;otherwise&gt; 会生效。

```xml
<select id="queryUser">
    select * from users
    where state = 'ACTIVE'
    <choose>
        <when test="title != null">and title = #{title}</when>
        <when test="content != null">and content = #{content}</when>
        <otherwise>and owner = "owner1"</otherwise>
    </choose>
</select>
```

### 标签属性

&lt;choose&gt; 和 &lt;otherwise&gt; 不含有任何属性。&lt;when&gt; 属性如下：

| 属性名  | 描述                                                     |
|------|--------------------------------------------------------|
| test | 必选，一个 ognl 表达式，表达式值是 boolean 表示判断是否成立，若判断成立则执行 SQL 生成。 |

## trim (where, set) 标签 {#trim}

&lt;trim&gt;、&lt;where&gt;、&lt;set&gt; 三个标签可以帮助我们在生成特定 SQL 语句时不会造成纰漏。在介绍它们三个之前看一下这个例子：

```xml
<select id="queryUser">
    select * from users
    where
    <if test="age != null">
        and age = #{age}
    </if>
</select>
```

当 `age` 属性为空时会生成如下 SQL。

```sql
select * from users
where
```

这是一个无效 SQL，若想避免此类问题可以选择 &lt;where&gt; 。

### where 标签（例：1）

当 &lt;if&gt; 条件成立 &lt;where&gt; 中会输出有效内容，一旦出现有效内容 &lt;where&gt; 会自动增加 where 语句。

```xml
<select id="queryUser">
    select * from users
    <where>
        <if test="age != null">
            age = #{age}
        </if>
    </where>
</select>
```

```sql title='根据 age 值不同，生成的语句将会是下列两种：'
select * from users;
select * from users where and age = ?
```

### where 标签（例：2）

下面这个例子 &lt;where&gt; 中多个条件，如果第一个条件匹配失败有可能出现 and 作为开头的情况。
而 &lt;where&gt; 将会自动检测如果是 `and` 或 `or` 开头语句则自动去掉它们。

```xml
<select id="queryUser">
    select * from users
    <where>
        <if test="age != null">
            age = #{age}
        </if>
        <if test="name != null">
            and name = #{name}
        </if>
    </where>
</select>
```

### set 标签

对于动态更新列语句可以选择 &lt;set&gt;，例如：

```xml
<update id="queryUser">
    update users
    <set>
        <if test="age != null">age=#{age},</if>
        <if test="name != null">name=#{name},</if>
    </set>
    where id=#{id}
</update>
```

### trim 标签

&lt;trim&gt; 可以替代 &lt;where&gt; 和 &lt;set&gt;，它的设计目标是为了解决一些特定场景。

```xml title='使用 trim 标签替代 where 标签'
<trim prefix="where" prefixOverrides="and | or">
    ...
</trim>
```

```xml title='使用 trim 标签替代 set 标签'
<trim prefix="set" suffixOverrides=",">
    ...
</trim>
```

**trim 标签的属性**

| 属性名             | 描述                                                             |
|-----------------|----------------------------------------------------------------|
| prefix          | <TagGray/> trim 在生成 SQL 时候的前缀。                                 |
| suffix          | <TagGray/> trim 在生成 SQL 时候的尾缀。                                 |
| prefixOverrides | <TagGray/> 当生成的 SQL 前几个字符匹配这个属性时会被自动裁剪掉，若要配置多个匹配项。需要用 竖线 来分割。  |
| suffixOverrides | <TagGray/> 当生成的 SQL 后面几个字符匹配这个属性时会被自动裁剪掉，若要配置多个匹配项。需要用 竖线 来分割。 |

## foreach 标签 {#foreach}

&lt;foreach&gt; 是常用的一个标签，通常是用来构建 `in` 语句或者 `values` 的值列表。

```xml
<select id="queryByIds">
  select * from users
  where id in
  <foreach item="item" index="index" collection="list"
           open="(" separator="," close=")">
    #{item}
  </foreach>
</select>
```

:::info[单纯的 foreach 可以使用规则来替代，例如：]
```xml
<select id="queryByIds">
    select * from users where @{in,id in :list}
</select>
```
- 了解 [IN 规则](../../rules/dynamic_rule#in)
:::

**foreach 标签的属性**

| 属性名        | 描述                                                    |
|------------|-------------------------------------------------------|
| collection | <TagRed/> 要遍历的数据，可以是数组、集合。                            |
| item       | <TagRed/> 在遍历数据过程中，用于标识当前元素的变量名。在标签中可以通过使用这个变量名访问到元素。 |
| open       | <TagGray/> 在开始遍历时候的输出到动态 SQL 的前缀部分。                   |
| close      | <TagGray/> 在开始遍历时候的输出到动态 SQL 的尾缀部分。                   |
| separator  | <TagGray/> 在遍历的过程中，用于区分每个元素的间隔字符。                     |

## bind 标签 {#bind}

&lt;bind&gt; 允许通过 `ognl` 表达式创建一个变量并将其绑定到上下文。例如:

```xml
<select id="queryByLike">
    <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
    select * from users
    where title like #{pattern}
</select>
```

**bind 标签的属性**

| 属性名   | 描述                                                    |
|-------|-------------------------------------------------------|
| name  | <TagRed/> 要遍历的数据，可以是数组、集合。                            |
| value | <TagRed/> 在遍历数据过程中，用于标识当前元素的变量名。在标签中可以通过使用这个变量名访问到元素。 |
